﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace RegExStudy
{
    /// <summary>
    /// 重复匹配
    /// </summary>
    public class Chapter5
    {
        // +  则表示匹配连续的字符，没有数量限制(1个或者多个，不匹配0个)
        // * 的规则是可以 匹配0个或者多个 = 如果没有也没关系
        // ? 匹配一次或零次，不匹配多次
        // {n}  特定数量
        // {min,max} 区间范围
        // {min,}  至少重复 min

        /// <summary>
        /// 匹配一个或多个字符 + 加号的使用
        /// </summary>
        public void Check1()
        {
            // 假设需要匹配邮箱地址
            // 任意数字字母 + @ + 任意数字字母 + . + 任意数字字母(还是说用com呢？)
            string str = " 456rew@163.com \n  rewfdas.456.com \n  ppod@55.com \n  uiojl@.com \n  hu.k@qq.com \n ";

            // \w 原本只是匹配单个字符上是否为 任意数字字母
            // 在后面添加  +   ->  \w+  则表示匹配连续的字符，没有数量限制(1个或者多个，不匹配0个)

            //第一版
            //var arry = Regex.Matches(str, @"\w+@\w+\.\w+");
            //Console.WriteLine(string.Format("Check1 查询 文本 \n {0} \n匹配字符 {1} ", str, @"\w+@\w+\.\w+"));

            //第二版
            // 考虑到 有部分邮箱 @ 前不仅有任意数字字母 还有符号 . 这个时候可以用方括号 [] ，把需要查找的字符都括起来
            // 需要注意的是 + 要在 [] 外，因为 [] 内也理解为对当个字符的匹配，+ 是对这个匹配规则的连续匹配
            var arry = Regex.Matches(str, @"[\w.]+@[\w.]+\.\w+");
            Console.WriteLine(string.Format("Check1 查询 文本 \n {0} \n匹配字符 {1} ", str, @"\w+@\w+\.\w+"));

            Console.WriteLine("匹配的数量 ： " + arry.Count);
            foreach (var item in arry)
            {
                Console.WriteLine(string.Format("分别为 {0} ", item.ToString()));
            }
            Console.WriteLine();
        }

        /// <summary>
        /// 匹配零个或多个字符 * 的使用
        /// </summary>
        public void Check2()
        {
            // * 的规则是可以 匹配0个或者多个 = 如果没有也没关系
            // * 与 + 的最大区别就是 * 是可以容忍找不到(差不多意思)
            string str = " 456rew@163.com \n  rewfdas.456.com \n  ppod@55.com \n  uiojl@.com \n  hu.k@qq.com \n ";

            var arry = Regex.Matches(str, @"[\w.]*@[\w.]*\.\w+");
            Console.WriteLine(string.Format("Check2 查询 文本 \n {0} \n匹配字符 {1} ", str, @"\w+@\w+\.\w+"));

            //  打印后会发现   uiojl@.com 也出现了，因为在 @后面 [\w.]*  允许了可以没有匹配对应的字母数字
            Console.WriteLine("匹配的数量 ： " + arry.Count);
            foreach (var item in arry)
            {
                Console.WriteLine(string.Format("分别为 {0} ", item.ToString()));
            }
            Console.WriteLine();
        }

        /// <summary>
        /// 匹配零个或一个字符 ? 的使用
        /// </summary>
        public void Check3()
        {
            // ? 匹配一次或零次，不匹配多次
            string str = " http://das.rewrq.com/  \n https://qqq.456.com/  \n  httpss:/lala.j.com/ ";

            // 这里在 http  s 后使用了 ? ，表示 s 匹配一次或零次
            var arry = Regex.Matches(str, @"https?://[\w./]+");
            Console.WriteLine(string.Format("Check3 查询 文本 \n {0} \n匹配字符 {1} ", str, @"https?://[\w./]+"));
            Console.WriteLine("匹配的数量 ： " + arry.Count);
            foreach (var item in arry)
            {
                Console.WriteLine(string.Format("分别为 {0} ", item.ToString()));
            }
            Console.WriteLine();
        }

        /// <summary>
        /// 为重复匹配次数设定一个精确的值  {}  的使用
        /// </summary>
        public void Check4()
        {
            //上面使用过的  +  *  ?   匹配的要么是零个 一个 或者多个，并没有精确指定的数量
            //表示指定重复的次数，可以使用  {}  大括号  比如 {3} 意味着需要匹配三个
            //假如匹配一个 颜色值

            string str = "#FFFFFF  \n  #66001F  \n  #000000F   \n  #FF010   ";

            // 如果只用旧的知识 就会一个一个得匹配单个字符
            //第一版
            //var arry = Regex.Matches(str, @"#[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]");
            //Console.WriteLine(string.Format("Check2 查询 文本 \n {0} \n匹配字符 {1} ", str, @"#[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]"));

            //第二版  #[0-9A-Fa-f]{6}  {6}  则表示按这个规则匹配6次
            var arry = Regex.Matches(str, @"#[0-9A-Fa-f]{6}");
            Console.WriteLine(string.Format("Check4 查询 文本 \n {0} \n匹配字符 {1} ", str, @"#[0-9A-Fa-f]{6}"));

            Console.WriteLine("匹配的数量 ： " + arry.Count);
            foreach (var item in arry)
            {
                Console.WriteLine(string.Format("分别为 {0} ", item.ToString()));
            }
            Console.WriteLine();
        }

        /// <summary>
        /// 为重复匹配次数设定一个区间
        /// </summary>
        public void Check5()
        {
            // {}  还可以设定一个匹配区间   {mix,max}  最少~最多
            // 假设查找日期
            // 年-月-日  或者  年/月/日
            string str = " 03/2/3   \n  22-4-1  \n  19-12-05  \n  1/1/1  \n  13-20-88   \n  01/20/1  ";

            // \/  因为斜杆是特殊字符，所以需要用反斜杆做转义 
            var arry = Regex.Matches(str, @"\d{2,4}[-\/]\d{1,2}[-\/]\d{1,2}");
            // 需要注意的是，这里只是匹配了格式的正确性，没有对数值的大小做了限制
            Console.WriteLine(string.Format("Check5 查询 文本 \n {0} \n匹配字符 {1} ", str, @"\d{2,4}[-\/]\d{1,2}[-\/]\d{1,2}"));
            
            Console.WriteLine("匹配的数量 ： " + arry.Count);
            foreach (var item in arry)
            {
                Console.WriteLine(string.Format("分别为 {0} ", item.ToString()));
            }
            Console.WriteLine();
        }

        /// <summary>
        /// 匹配 至少重复多次
        /// </summary>
        public void Check6()
        {
            // {mix,}  mix 则表示至少需要重复的次数 ，但是没有上限
            // 匹配大于或等于 100 美元的金额
            string str = " $20.12  \n  $6.0  \n  $9999.2  \n  $200.88   \n  $199.199";
            // 这里是百位至少是3位数，小数点保留 2位小数 
            var arry = Regex.Matches(str, @"\$\d{3,}\.\d{2}");
            Console.WriteLine(string.Format("Check6 查询 文本 \n {0} \n匹配字符 {1} ", str, @"\$\d{3,}\.\d{1,2}"));

            // 会发现小数掉后三位的也匹配进来了
            Console.WriteLine("匹配的数量 ： " + arry.Count);
            foreach (var item in arry)
            {
                Console.WriteLine(string.Format("分别为 {0} ", item.ToString()));
            }
            Console.WriteLine();
        }

        /// <summary>
        /// 防止匹配过度
        /// </summary>
        public void Check7()
        {
            // ?  和 {}  {mix,max} 都是有重复上限的，但是其他的 +  * {min,} 等没有限制，这会导致不可控的情况出现

            // 比如匹配富文本中字符
            string str = @"<b>今夕</b>何夕兮，搴舟中流。<b>今日</b>何日兮，得与王子同舟。蒙羞被好兮，不訾诟耻。心几烦而<b>不绝兮</b>，得知王子。";

            // 找到 <b></b> 中加粗的文本内容

            // 第一版
            //var arry = Regex.Matches(str, @"<[Bb]>.*</[Bb]>");
            //// 这个时候会发现匹配的是最开始 <b> 到最后面的</b> 一整段
            //Console.WriteLine(string.Format("Check7 查询 文本 \n {0} \n匹配字符 {1} ", str, @"<[Bb]>.*</[Bb]>"));

            // 贪婪型元字符  与  懒惰型元字符
            // *  ->  *?
            // +  ->  +?
            // {mix,}  -> {mix,}?
            // 在末尾加上 ? 

            // 第二版
            var arry = Regex.Matches(str, @"<[Bb]>.*?</[Bb]>");
            Console.WriteLine(string.Format("Check7 查询 文本 \n {0} \n匹配字符 {1} ", str, @"<[Bb]>.*</[Bb]>"));

            Console.WriteLine("匹配的数量 ： " + arry.Count);
            foreach (var item in arry)
            {
                Console.WriteLine(string.Format("分别为 {0} ", item.ToString()));
            }
            Console.WriteLine();
        }
    }
}
